#pragma once

#include <AVUIVisual3D.h>

namespace AVUI {
class RayHitTestResult;
class Point3DCollection;
class PointCollection;
class Int32Collection;
class Material;
class Geometry3D;

/// <summary>Renders the 2-D children within the specified 3-D viewport bounds.</summary>
class Viewport2DVisual3D : public Visual3D
{
public:
    DECLARE_ELEMENT(Viewport2DVisual3D, Visual3D);

#pragma region Public Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Media.Media3D.Viewport2DVisual3D" /> class.</summary>
    Viewport2DVisual3D();

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets the value of the <see cref="P:System.Windows.Media.Media3D.Viewport2DVisual3D.IsVisualHostMaterial" /> attached property for a specified <see cref="T:System.Windows.UIElement" />.</summary>
    /// <returns>The <see cref="P:System.Windows.Media.Media3D.Viewport2DVisual3D.IsVisualHostMaterial" /> property value for the element.</returns>
    /// <param name="element">The element from which the property value is read.</param>
    static bool GetIsVisualHostMaterial(Material* pElement) { return  UnboxValue<bool>(((DependencyObject*)pElement)->GetValue(get_IsVisualHostMaterialProperty())); }

    /// <summary>Sets the value of the <see cref="P:System.Windows.Media.Media3D.Viewport2DVisual3D.IsVisualHostMaterial" /> attached property to a specified element.</summary>
    /// <param name="element">The element to which the attached property is written.</param>
    /// <param name="value">The required <see cref="P:System.Windows.Media.Media3D.Viewport2DVisual3D.IsVisualHostMaterial" /> value.</param>
    static void SetIsVisualHostMaterial(Material* pElement, bool value) { ((DependencyObject*)pElement)->SetValue(get_IsVisualHostMaterialProperty(), BoxValue(value)); }
    DECLARE_DEPENDENCY_PROPERTY(IsVisualHostMaterial);

    /// <summary>Gets or sets the 3-D geometry for this <see cref="T:System.Windows.Media.Media3D.Viewport2DVisual3D" />.</summary>
    /// <returns>The 3-D geometry for this <see cref="T:System.Windows.Media.Media3D.Viewport2DVisual3D" />.</returns>
    TRefCountedPtr<Geometry3D> get_Geometry() { return (Geometry3D*)(Object*)GetValue(get_GeometryProperty()); }
    void set_Geometry(Geometry3D* pValue) { SetValue(get_GeometryProperty(), (Object*)pValue); } 
    DECLARE_DEPENDENCY_PROPERTY(Geometry);

    /// <summary>Gets or sets the material that describes the appearance of the 3-D object.</summary>
    /// <returns>The material for the 3-D object.</returns>
    TRefCountedPtr<Material> get_Material() { return (Material*)(Object*)GetValue(get_MaterialProperty()); }
    void set_Material(Material* pValue) { SetValue(get_MaterialProperty(), (Object*)pValue); } 
    DECLARE_DEPENDENCY_PROPERTY(Material);

    /// <summary>Gets or sets the 2-D visual to be placed on the 3-D object.</summary>
    /// <returns>The visual to be placed on the 3-D object.</returns>
    TRefCountedPtr<Visual> get_Visual() { return (Visual*)(Object*)GetValue(get_VisualProperty()); }
    void set_Visual(Visual* pValue) { SetValue(get_VisualProperty(), (Object*)pValue); } 
    DECLARE_DEPENDENCY_PROPERTY_BEGIN(Visual);
        DECLARE_ATTRIBUTE_CONTENTPROPERTY()
    DECLARE_DEPENDENCY_PROPERTY_END()

#pragma endregion Public Properties

protected:

#pragma region Protected Methods
    virtual Visual3D* GetVisual3DChild(int index) AVUI_OVERRIDE { LibraryCriticalError(); return 0; }
#pragma endregion Protected Methods

#pragma region Protected Properties
    virtual int get_Visual3DChildrenCount() AVUI_OVERRIDE { return 0; }
#pragma endregion Protected Properties

public:

#pragma region Internal Methods

    static bool Get3DPointFor2DCoordinate(const Point& point, Point3D* pPoint3D, Point3DCollection* pPositions, PointCollection* pTextureCoords, Int32Collection* pTriIndices);
    static bool GetIntersectionInfo(RayHitTestResult* pRayHitResult, Point* pOutputPoint);
    virtual DependencyObject* InternalGet2DOr3DVisualChild(int index) AVUI_OVERRIDE;
    static void OnGeometryChanged(DependencyObject* pSender, DependencyPropertyChangedEventArgs* e);
    static void OnMaterialPropertyChanged(DependencyObject* pSender, DependencyPropertyChangedEventArgs* e);
    static void OnVisualChanged(DependencyObject* pSender, DependencyPropertyChangedEventArgs* e);
    static Point TextureCoordsToVisualCoords(const Point& uv, Visual* pVisual);
    static Point TextureCoordsToVisualCoords(const Point& uv, const Rect& descBounds);
    static Point VisualCoordsToTextureCoords(const Point& pt, Visual* pVisual);
    static Point VisualCoordsToTextureCoords(const Point& pt, const Rect& descBounds);

#pragma endregion Internal Methods
 
#pragma region Internal Properties

    Point3DCollection* get_InternalPositionsCache();
    void set_InternalPositionsCache(Point3DCollection* pValue);

    PointCollection* get_InternalTextureCoordinatesCache();
    void set_InternalTextureCoordinatesCache(PointCollection* pValue);

    Int32Collection* get_InternalTriangleIndicesCache();
    void set_InternalTriangleIndicesCache(Int32Collection* pValue);

    virtual int get_InternalVisual2DOr3DChildrenCount() AVUI_OVERRIDE;

#pragma endregion Internal Properties

private:
 
#pragma region Private Methods
    static void StaticConstructor();

    void AddVisualChild(Visual* pChild);
    TRefCountedPtr<VisualBrush> CreateVisualBrush();
    void GenerateMaterial();
    void InvalidateAllCachedValues();
    void RemoveVisualChild(Visual* pChild);
    void SwapInVisualBrush(Material* pMaterial);

#pragma endregion Private Methods

#pragma region Private Properties

    VisualBrush* get_InternalVisualBrush() { return m_pVisualBrush; }
    void set_InternalVisualBrush(VisualBrush* pValue) { m_pVisualBrush = pValue; }

#pragma endregion Private Properties

#pragma region Private Fields

    TRefCountedPtr<Point3DCollection> m_pPositionsCache;
    TRefCountedPtr<PointCollection> m_pTextureCoordinatesCache;
    TRefCountedPtr<Int32Collection> m_pTriangleIndicesCache;
    TRefCountedPtr<VisualBrush> m_pVisualBrush;

#pragma endregion Private Fields

};


}; // namespace AVUI

